
#include "stdafx.h"
#include "Qtrial_widget.h"

#include "apihijack.h"

static inline BOOL CALLBACK EnumWinProc(HWND hwnd,LPARAM outHwnd)
{
	HWND hWndShellDLL = FindWindowExW(hwnd, NULL, L"SHELLDLL_DefView", NULL);
	if (hWndShellDLL!=nullptr)
	{
		*((HWND*)outHwnd)=hWndShellDLL;
		return FALSE;
	}
	return TRUE;
}

static inline void KeepOnDeskTop(HWND hWnd)
{
	HWND hWndProgram = NULL;
	HWND hWndShellDLL = NULL;
	hWndProgram = FindWindowW(L"Progman", L"Program Manager");
	if(hWndProgram != NULL)
	{
		hWndShellDLL = FindWindowExW(hWndProgram, NULL, L"SHELLDLL_DefView", NULL);
	}
	if(hWndShellDLL == NULL)
	{
		EnumWindows(EnumWinProc,(LPARAM)&hWndShellDLL);
	}	
	//::SetParent(hWnd,hWndShellDLL);
	SetWindowLongPtrW(hWnd, GWL_HWNDPARENT, (LONG)hWndShellDLL);
	::SetWindowPos(hWnd,HWND_BOTTOM ,0,0,0,0,SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOMOVE | SWP_NOSIZE|SWP_FRAMECHANGED);
}


//////////////////////////////////////////////////////////////////////////
// View
class Qtrial_QuickView:public QQuickView
{
public:
	Qtrial_QuickView(QWindow* parent=0);
	void setMainQmlFile(const QString &file);
	void addImportPath(const QString &path);
	QImage getImage();
private:
	QString adjustPath(const QString &path);
	virtual void resizeEvent( QResizeEvent * e);
private:
	std::unique_ptr<QOpenGLFramebufferObject>	_fbo;
	friend class Qtrial_Widget;
};

inline QString Qtrial_QuickView::adjustPath( const QString &path )
{
	QString pathInInstallDir =QString::fromLatin1("%1/../%2").arg(QDir::currentPath(), path);
	if (QFileInfo(pathInInstallDir).exists())
		return pathInInstallDir;
	pathInInstallDir =QString::fromLatin1("%1/%2").arg(QDir::currentPath(), path);
	if (QFileInfo(pathInInstallDir).exists())
		return pathInInstallDir;
	return path;
}

inline void Qtrial_QuickView::addImportPath( const QString &path )
{
	engine()->addImportPath(adjustPath(path));
}

inline void Qtrial_QuickView::setMainQmlFile( const QString &file )
{
	setSource(QUrl::fromLocalFile(adjustPath(file)));
}

inline QImage Qtrial_QuickView::getImage()
{
	if (_fbo)
	{
		return _fbo->toImage();
	}
	return QImage();
}

void Qtrial_QuickView::resizeEvent( QResizeEvent * e )
{
	QQuickView::resizeEvent(e);
	QOpenGLFramebufferObjectFormat ft;
	ft.setAttachment(QOpenGLFramebufferObject::Attachment::Depth);
	_fbo.reset(new QOpenGLFramebufferObject(e->size(),ft));
	setRenderTarget(_fbo.get());
}

inline Qtrial_QuickView::Qtrial_QuickView( QWindow* parent/*=0*/ ) :QQuickView(parent)
{
	setFlags(Qt::FramelessWindowHint);
	setResizeMode(QQuickView::SizeRootObjectToView);
	setColor(QColor(Qt::transparent));
	//KeepOnDeskTop((HWND)this->winId());
	//connect(this,&QQuickView::afterRendering,[=]	{QOpenGLFramebufferObject::blitFramebuffer(0,_fbo.get());}	);
}




//////////////////////////////////////////////////////////////////////////
//
#pragma region HOOK

#ifndef NDEBUG
#define DllName(x) (x"d"".dll")
#else
#define DllName(x) (x ".dll")
#endif


static std::map<QWindow*,QQuickView*> _widgetView;

static QWindow* focusHook();
static SDLLHook _Hook_Data = 
{
	DllName("Qt5Gui"),
	false, NULL,		// Default hook disabled, NULL function pointer.
	{
		{ "?focusWindow@QGuiApplication@@SAPAVQWindow@@XZ", focusHook },
		{ NULL, NULL }
	}
};

// hook function
static QWindow* focusHook()
{
	typedef  QWindow* (*Type)() ;
	auto w=static_cast<Type>(_Hook_Data.Functions[0].OrigFn)();

	if (_widgetView.find(w)!=_widgetView.end())
	{
		return _widgetView[w];
	}
	return w;
}

void InstallFocusHook()
{
	if(!HookAPICalls(&_Hook_Data,DllName("Qt5Quick") ))
	{
		throw std::runtime_error("can't hook QtQuick");
	}
}



#pragma endregion HOOK



//////////////////////////////////////////////////////////////////////////
//
class Qtrial_WidgetPrivate
{

public:
	Qtrial_WidgetPrivate()
		:_size(false)
	{
		_quickview.reset(new Qtrial_QuickView());
	}

private:
	std::unique_ptr<Qtrial_QuickView>			_quickview;
	bool										_size;

	friend class Qtrial_Widget;
};



//////////////////////////////////////////////////////////////////////////
//
//
//
Qtrial_Widget::Qtrial_Widget( QWidget* parent/*=0*/ ) :QWidget(parent)
{
	setWindowTitle("QWidget");
	setWindowFlags(Qt::FramelessWindowHint);
	setAttribute(Qt::WA_TranslucentBackground);
	setAcceptDrops(true);
	setMouseTracking(true);
	setFocusPolicy(Qt::FocusPolicy::WheelFocus);
	winId();

	//KeepOnDeskTop((HWND)this->winId());

	_private.reset(new Qtrial_WidgetPrivate());

	_widgetView[this->windowHandle()]=_private->_quickview.get();

	if (_private->_quickview->size().isEmpty())
	{
		_private->_quickview->installEventFilter(this);
	}
	else
	{
		resize(_private->_quickview->size());
		_private->_size=true;
	}

	connect(_private->_quickview.get(),&QQuickView::frameSwapped,[=]{
		this->update();
	});
}

Qtrial_Widget::~Qtrial_Widget()
{
	_widgetView.erase(this->windowHandle());
}

bool Qtrial_Widget::event(QEvent * e)
{
	switch (e->type())
	{
	case QEvent::Close:
		{
			_private->_quickview->close();
		}break;
	case QEvent::Show:
		{
			_private->_quickview->show();
		}break;
	case QEvent::Resize:
		{
			if (_private->_size)
			{
				_private->_quickview->resize(width(),height());
			}
		}break;
	case QEvent::Move:
		{
			if(_private->_quickview->isVisible())
			{
				_private->_quickview->setPosition(
					this->pos().x()+QApplication::desktop()->screenGeometry().width()*10,
					geometry().top());
			}
		}break;
	case QEvent::Paint:
		{
			QPainter p;
			p.begin(this);
			p.fillRect(QRect(0,0,width(),height()),Qt::transparent);
			p.drawImage(0,0,_private->_quickview->getImage());
			p.end();
		}break;
	case QEvent::MouseButtonPress:
	case QEvent::MouseButtonRelease:
	case QEvent::MouseButtonDblClick:
	case QEvent::MouseMove:
	case QEvent::Leave:
		{
			QPointF localPos(static_cast<QMouseEvent*>(e)->localPos());
			QPointF windowPos(static_cast<QMouseEvent*>(e)->windowPos());
			QMouseEvent mouseEvent(e->type(), localPos, windowPos,
				static_cast<QMouseEvent*>(e)->button(),
				static_cast<QMouseEvent*>(e)->buttons(),
				static_cast<QMouseEvent*>(e)->modifiers());
			QCoreApplication::sendEvent(_private->_quickview.get(), &mouseEvent);
			//return true;
		}break;
	case QEvent::FocusIn://_quickview->show();
	case QEvent::FocusOut:
	case QEvent::FocusAboutToChange:
	case QEvent::DragEnter:
	case QEvent::DragLeave:
	case QEvent::DragMove:
	case QEvent::Drop:

	case QEvent::KeyPress:
	case QEvent::KeyRelease:
	case QEvent::Wheel:
		{
			QCoreApplication::sendEvent(_private->_quickview.get(), e);
			return true;
		}break;
	default:
		break;
	}
	return QWidget::event(e);
}

bool Qtrial_Widget::eventFilter( QObject * o, QEvent * e )
{
	Q_ASSERT(o==_private->_quickview.get());
	switch (e->type())
	{
	case QEvent::Resize:
		{
			resize(static_cast<QResizeEvent*>(e)->size());
			_private->_size=true;
			_private->_quickview->removeEventFilter(this);
		}	break;
	default:
		break;
	}
	return false;
}

void Qtrial_Widget::setMainQmlFile( const QString &file )
{
	_private->_quickview->setMainQmlFile(file);
}

void Qtrial_Widget::addImportPath( const QString &path )
{
	_private->_quickview->addImportPath(path);

}


